﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using SimpleIR;
using System.Diagnostics.Contracts;

namespace RgnAnalyzer.Domain
{
	/// <summary>
	/// This struct tracks the info for a single entry of an allocated type/location/identity that reaches this region.
	/// </summary>
	internal struct EscapeSiteInfo : IEquatable<EscapeSiteInfo>, IComparable<EscapeSiteInfo>
	{
		internal static int EscapeIdentityCounter = -1;

		/// <summary>
		/// This is the most recent escape, or alloction site associated with this object.
		/// </summary>
		private readonly int m_escline;

		/// <summary>
		/// This tracks the flow time of the alloc/escape in terms of loop current/previous iterations -- initially set to the line
		/// </summary>
		private readonly int m_escflowlooptime;

		/// <summary>
		/// This is the special escape identity we give and use during the analysis completion phase.
		/// </summary>
		private readonly int m_escidentity;

		/// <summary>
		/// Create a <c>EscapeSiteInfo</c> with the given values.
		/// </summary>
		internal EscapeSiteInfo(int escline, int escflowloop, int escidentity)
		{
			this.m_escline = escline;
			this.m_escflowlooptime = escflowloop;
			
			this.m_escidentity = escidentity;
		}

		/// <summary>
		/// For updating the loop escape flow time (checks if the escape flow time is in the range and returns the 
		/// appropriate info).
		/// </summary>
		internal EscapeSiteInfo UpdateLoopFlowLineAsNeeded(int minline, int maxline, int newline)
		{
			if((minline <= this.m_escflowlooptime) & (this.m_escflowlooptime <= maxline))
				return new EscapeSiteInfo(this.m_escline, newline, this.m_escidentity);
			else
				return this;
		}

		public override string ToString()
		{
			return this.m_escline.ToString() + ":" + this.m_escflowlooptime.ToString() + "@" + this.m_escidentity.ToString();
		}

		public static bool operator ==(EscapeSiteInfo um1, EscapeSiteInfo um2)
		{ return um1.Equals(um2); }

		public static bool operator !=(EscapeSiteInfo um1, EscapeSiteInfo um2)
		{ return !um1.Equals(um2); }

		public static bool operator <(EscapeSiteInfo um1, EscapeSiteInfo um2)
		{ return um1.CompareTo(um2) < 0; }

		public static bool operator >(EscapeSiteInfo um1, EscapeSiteInfo um2)
		{ return um1.CompareTo(um2) > 0; }

		public override int GetHashCode()
		{ return (this.m_escline ^ this.m_escflowlooptime ^ this.m_escidentity); }

		public override bool Equals(Object obj)
		{ return (obj is EscapeSiteInfo) && this.Equals((EscapeSiteInfo)obj); }

		public bool Equals(EscapeSiteInfo other)
		{ return (this.m_escline == other.m_escline) & (this.m_escflowlooptime == other.m_escflowlooptime) & (this.m_escidentity == other.m_escidentity); }

		public int CompareTo(EscapeSiteInfo other)
		{
			int valescl = this.m_escline - other.m_escline;
			if(valescl != 0)
				return valescl;
			else
			{
				int valescf = this.m_escflowlooptime - other.m_escflowlooptime;
				return (valescf != 0) ? valescf : this.m_escidentity - other.m_escidentity;
			}
		}

		/// <summary>
		/// Return the escape line.
		/// </summary>
		internal int EscapeLine
		{ get { return this.m_escline; } }

		/// <summary>
		/// Return the escape loop flow time/line.
		/// </summary>
		internal int EscapeLoopFlow
		{ get { return this.m_escflowlooptime; } }

		/// <summary>
		/// Return the escape identity.
		/// </summary>
		internal int EscapeIdentity
		{ get { return this.m_escidentity; } }
	}
}
